/*-----------------------------------------------------------------------
Parser.y - Created by CompilerGroup
Reference from SDCC
-------------------------------------------------------------------------*/
%{
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "common.h"
#include "iCode.h"

STACK_DCL (AndOrStack, int, MAX_DCL_STACK);
STACK_DCL (BreakStack, int, MAX_DCL_STACK);
STACK_DCL (BreakLabelStack, char *, MAX_DCL_STACK); 
STACK_DCL (ContinueStack, int, MAX_DCL_STACK);
STACK_DCL (ContinueLabelStack, char *, MAX_DCL_STACK);
STACK_DCL (SwitchStack,struct stab *, MAX_DCL_STACK);

/* added by nttrung */
STACK_DCL (blockNum, int, MAX_DCL_STACK);

FILE *pFileRule;
extern int yyerror (char *);
extern FILE     *yyin;
int NestLevel = 0 ;     /* current NestLevel       */
int stackPtr  = 0 ;     /* stack pointer           */
int reentrant = 0 ;
int blockNo   = 0 ;     /* sequential block number  */
int currBlockno=0 ;
int inCritical= 0 ;
int seqPointNo= 1 ;     /* sequence point number */
char labelbuffer[MAX_LENGTH_BUFFER];
int ignoreTypedefType=0;
extern int yylex();
extern char *yytext;
int yyparse(void);
char lbuff[1024];      /* local buffer */
int	Case_label = 0;
/* break & continue stacks */
bool uselessDecl = TRUE;

#define YYDEBUG 1

%}
%expect 6

%union {
    struct symbol	*sym;      
	struct value	*val;
	struct operand  *op;
	struct sym_link   *lnk ;     
    char	yychar[255];
    int		yyint;     
	const char *yyinline;
    //initList   *ilist;   
	struct structdef  *sdef;
}

%token <yychar> IDENTIFIER TYPE_NAME
%token <val> CONSTANT STRING_LITERAL
%token SIZEOF TYPEOF
%token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
%token AND_OP OR_OP
%token <yyint> MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
%token <yyint> SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN
%token <yyint> XOR_ASSIGN OR_ASSIGN
%token TYPEDEF EXTERN STATIC AUTO REGISTER 
%token AT SBIT VAR_ARGS 
%token CHAR SHORT INT LONG SIGNED UNSIGNED FLOAT DOUBLE CONST VOLATILE VOID BIT
%token STRUCT UNION ENUM 
%token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN
%token IFX ADDRESS_OF GET_VALUE_AT_ADDRESS SPIL UNSPIL GETHBIT GETABIT GETBYTE GETWORD
%token BITWISEAND UNARYMINUS IPUSH IPOP PCALL  ENDFUNCTION JUMPTABLE
%token RRC RLC
%token CAST CALL PARAM  LABEL RECEIVE SEND ARRAYINIT
%token INLINE 

%type <sym> identifier declarator declarator2 declarator3
%type <sym> struct_declarator function_declarator function_declarator2
%type <sym> struct_declarator_list struct_declaration struct_declaration_list
%type <sym> declaration init_declarator_list init_declarator
%type <sym> declaration_list identifier_list
%type <sym> declarator2_function_attributes
%type <lnk> pointer type_specifier_list type_specifier_list_ type_specifier type_name
%type <lnk> storage_class_specifier struct_or_union_specifier function_specifier
%type <lnk> declaration_specifiers declaration_specifiers_ type_specifier2
%type <lnk> enum_specifier
%type <lnk> abstract_declarator abstract_declarator2 unqualified_pointer
%type <val> parameter_type_list parameter_list parameter_declaration opt_assign_expr
%type <op> logical_or_expr__ logical_and_expr__
%type <op> primary_expr
%type <op> postfix_expr unary_expr cast_expr multiplicative_expr
%type <op> additive_expr shift_expr relational_expr equality_expr
%type <op> and_expr exclusive_or_expr inclusive_or_expr logical_or_expr
%type <op> logical_and_expr conditional_expr assignment_expr constant_expr
%type <op> expr argument_expr_list function_definition expr_opt
%type <op> statement_list statement labeled_statement compound_statement
%type <op> expression_statement selection_statement iteration_statement
%type <op> jump_statement function_body string_literal
%type <sdef> stag opt_stag
%type <op> initializer
%type <yyint> unary_operator assignment_operator struct_or_union
%type <yyint> test
%start file

%%

file
   : /* empty */
        { 
			yylexerror("Empty source code");
        }
   | program	{ 	reverseiCChain(); }
   ;

program
   : external_definition
   | program external_definition
   ;

external_definition
   : function_definition     {  blockNo = 0; }
   | declaration             
							{							   
							   ignoreTypedefType = 0;
                               if ($1 && $1->type
                                && IS_FUNC($1->type))
                               {
                                   if ($1->etype
                                    && IS_SPEC($1->etype)
                                    && !SPEC_STAT($1->etype))
                                   {
                                        SPEC_EXTR($1->etype) = 1;
                                   }
                               }
                               
                               addSymChain (&$1); 
							   allocVariables($1);                                                             							  
						   }
   ;

function_definition
   : function_declarator
	{ 
		symbol *sym;
		addDecl($1,0, newIntLink());	
  		sym = createFunction($1);

		geniCodeFunction(operandFromSymbol(sym));
	}
	function_body  
	{
		geniCodeEndFunction();
	}
   | declaration_specifiers function_declarator 
	{
		symbol *sym;
        pointerTypes($2->type,copyLinkChain($1));
        addDecl($2,0,$1);	
        
  		/* add symbol to table */
  		sym = createFunction($2);    
		
		geniCodeFunction(operandFromSymbol(sym));
	}
   function_body
	{ 
		geniCodeEndFunction();
	}
   ;

function_body
   : compound_statement
   | declaration_list compound_statement
		{
            yylexerror("Old style: %s", ($1 ? $1->name : ""));
            exit(1);		
		}
   ;


primary_expr
   : identifier      
		{ 			
			symbol *sym, *sym1;
			char *sen = $1->name;
			sym = findSym(TypedefTab, $1, $1->name);			
			if (sym)
				yylexerror("duplicate identifier name '%s' and typedef type '%s'", $1->name, ((symbol *)sym)->name);								
			else
				sym = findSymWithLevel(SymbolTab, $1);
				
			if (sym &&
			    (sym->islocal || sym->_isparm) &&
			    strcmp(sym->localof->name, currFunc->name) != 0)
			{
				sym1 = $1;
				sym1->level = 0;				
				sym = findSymWithLevel(SymbolTab, sym1);
			}
							
			if (!sym)
			{
				yylexerror("undefined identifier '%s'", $1->name);
				$$ = NULL;
			}
			else
			{
				$$ = operandFromSymbol(sym);
			}
		}
   | CONSTANT        
		{
			$$ = operandFromValue($1);
		}
   | string_literal 
   | '(' expr ')'    
		{
			$$ = $2; 			
		}
   ;

string_literal
    : STRING_LITERAL                    
    {
		$$ = operandFromValue($1);
    }
    ;

postfix_expr
   : primary_expr
   | postfix_expr '[' expr ']'         	
		{
			if ($1 && $3)
				$$ = geniCodeArray($1, $3);
			else
				$$ = NULL;
		}
   | postfix_expr '(' ')'              	
		{
			if ($1)
				$$ = geniCodeCall($1, NULL);
			else
				$$ = NULL;
		}
   | postfix_expr '(' argument_expr_list ')'	
		{
			if ($1)
				$$ = geniCodeCall($1, $3);
			else
				$$ = NULL;
		}    
   | postfix_expr '.' { ignoreTypedefType = 1; } identifier
		{ 
			ignoreTypedefType = 0;
			$4 = newSymbol($4->name,NestLevel);
			if ($1)
				$$ = geniCodeStruct ($1, operandFromSymbol($4));
			else
				$$ = NULL;
		}
   | postfix_expr PTR_OP { ignoreTypedefType = 1; } identifier
        { 
			// PTR_OP means '->'
			ignoreTypedefType = 0;
			$4 = newSymbol($4->name,NestLevel);
			if ($1)
				$$ = geniCodeStruct ($1, operandFromSymbol($4));
			else
				$$ = NULL;
		}
   | postfix_expr INC_OP
        {
			if ($1)
				$$ = geniCodePostInc($1);
			else
				$$ = NULL;
		}
   | postfix_expr DEC_OP
		{
			if ($1)
				$$ = geniCodePostDec($1);
			else
				$$ = NULL;
		}
   ;

/* commented by nvcuong : pass arguments */
argument_expr_list
   : assignment_expr
   | assignment_expr ',' argument_expr_list 
	{ 
		$1->next = $3;
		$$ = $1;
	}
   ;


unary_expr
   : postfix_expr				
   | INC_OP unary_expr        	
		{ 
			if ($2)
				$$ = geniCodePreInc($2);
			else 
				$$ = NULL;
		}
   | DEC_OP unary_expr        	
		{ 
			if ($2)
				$$ = geniCodePreDec($2);
			else
				$$ = NULL;
		}
   | unary_operator cast_expr 	
		{
			if (!$2)
				$$ = NULL;
			else
			{
				if ($1=='-')
					$$ = geniCodeUnaryMinus($2);
				else if ($1=='*')
					$$ = geniCodeDerefPtr($2, NestLevel);
				else if ($1=='&')
					$$ = geniCodeAddressOf($2);	
				else
					$$ = geniCodeUnary($2, $1);
			}
		}
   | SIZEOF unary_expr        	
		{ 
				
		}
   | SIZEOF '(' type_name ')' 	
		{ 
			
		}
   | TYPEOF unary_expr        	
		{ 
			
		}
   ;

unary_operator
   : '&'    { $$ = '&';}
   | '*'    { $$ = '*';}
   | '+'    { $$ = '+';}
   | '-'    { $$ = '-';}
   | '~'    { $$ = '~';}
   | '!'    { $$ = '!';}
   ;

cast_expr
   : unary_expr 
   | '(' type_name ')' cast_expr 
		{ 
			if ($2 && $4)
				$$ = geniCodeCast($2, $4, false);
			else
				$$ = NULL;
		}
   ;

multiplicative_expr
   : cast_expr	
   | multiplicative_expr '*' cast_expr 
		{ 
			if ($1 && $3)
				$$ = geniCodeMultiply($1, $3);
			else
				$$ = NULL;
		}
   | multiplicative_expr '/' cast_expr 
		{ 
			if ($1 && $3)
				$$ = geniCodeDivision($1, $3);
			else
				$$ = NULL;
		}
   | multiplicative_expr '%' cast_expr 
		{ 
			if ($1 && $3)
				$$ = geniCodeModulus($1, $3);
			else
				$$ = NULL;
		}
   ;

additive_expr
   : multiplicative_expr 
   | additive_expr '+' multiplicative_expr 
   { 
		if ($1 && $3)
				$$ = geniCodeAdd($1, $3, false);
		else
			$$ = NULL;
   }
   | additive_expr '-' multiplicative_expr 
	{
		if ($1 && $3)
			$$ = geniCodeSubtract($1, $3);
		else
			$$ = NULL;	
	}
   ;

shift_expr
   : additive_expr  { $$ = $1;}
   | shift_expr LEFT_OP additive_expr   
		{ 
			if ($1 && $3)
				$$ = geniCodeLeftShift($1, $3);
			else
				$$ = NULL;
		}
   | shift_expr RIGHT_OP additive_expr  
		{ 
			if ($1 && $3)
				$$ = geniCodeRightShift($1, $3);
			else
				$$ = NULL;
		}
   ;

relational_expr
   : shift_expr   
   | relational_expr '<' shift_expr   
		{ 
			if ($1 && $3)
				$$ = geniCodeLogic($1, $3, '<');
			else
				$$ = NULL;
		}
   | relational_expr '>' shift_expr   
		{ 
			if ($1 && $3)
				$$ = geniCodeLogic($1, $3, '>');
			else
				$$ = NULL;
		}
   | relational_expr LE_OP shift_expr 
		{ 
			if ($1 && $3)
				$$ = geniCodeLogic($1, $3, LE_OP);
			else
				$$ = NULL;
		}
   | relational_expr GE_OP shift_expr 
		{ 
			if ($1 && $3)
				$$ = geniCodeLogic($1, $3, GE_OP);
			else
				$$ = NULL;
		}
   ;

equality_expr
   : relational_expr
   | equality_expr EQ_OP relational_expr 
		{ 
			if ($1 && $3)
				$$ = geniCodeLogic($1, $3, EQ_OP);
			else
				$$ = NULL;
		}
   | equality_expr NE_OP relational_expr 
		{ 
			if ($1 && $3)
				$$ = geniCodeLogic($1, $3, NE_OP);
			else
				$$ = NULL;
		}
   ;

and_expr
   : equality_expr
   | and_expr '&' equality_expr  
		{ 
			if ($1 && $3)
				$$ = geniCodeBitwise($1, $3, '&');
			else
				$$ = NULL;
		}
   ;

exclusive_or_expr
   : and_expr
   | exclusive_or_expr '^' and_expr 
	{ 
		if ($1 && $3)
			$$ = geniCodeBitwise($1, $3, '^');
		else
			$$ = NULL;
	}
   ;

inclusive_or_expr
   : exclusive_or_expr
   | inclusive_or_expr '|' exclusive_or_expr 
	{ 
		if ($1 && $3)
			$$ = geniCodeBitwise($1, $3, '|');
		else
			$$ = NULL;
	}
   ;

logical_and_expr
   : inclusive_or_expr
   {
		$$ = $1;
		STACK_PUSH(AndOrStack, 0);
   }
   | logical_and_expr AND_OP 
		{
			if( $1 )
				createLogicalAnd($1, STACK_ITEM(AndOrStack,0) = tf_label());
		}
		inclusive_or_expr
		{ 
			if ($4)
				createLogicalAnd( $4, STACK_ITEM(AndOrStack,0) );
			$$ = NULL;
		}
   ;

logical_and_expr__
	: logical_and_expr
	{
		int label;
		if( label = STACK_POP(AndOrStack))
		{
			sprintf(labelbuffer, "%s%d", L_TRUE, label);
			geniCodeGotoFromName(labelbuffer);
			$$ = gen_false_true(label, NULL);
		}
		else
			$$ = $1;
	}
   
logical_or_expr
   : logical_and_expr__  
   {
		$$ = $1;
		STACK_PUSH(AndOrStack, 0);
   }
   | logical_or_expr OR_OP 
	{
		if( $1 )
			createLogicalOr($1, STACK_ITEM(AndOrStack,0) = tf_label());
	}
	logical_and_expr__
	{ 
		if ($4)
			createLogicalOr($4, STACK_ITEM(AndOrStack,0));
		$$ = NULL;
	}
   ;

logical_or_expr__
	:logical_or_expr
	{
		int label;
		if(label = STACK_POP(AndOrStack))
		{
			$$ = gen_false_true(label, NULL);
		}
		else
			$$ = $1;
	}
   
//  a>b?a:b
conditional_expr
   : logical_or_expr__
   | logical_or_expr '?' 
	{ 
		static int label = 0;
		struct symbol *_falseLab;
		seqPointNo++;
		sprintf(labelbuffer, "%s%d", L_COND_FALSE, $<yyint>$ = ++label);
		_falseLab = newSymbol(labelbuffer, NestLevel);
		if ($1)
			geniCodeIfx($1, NULL, _falseLab); 
	} 
	logical_or_expr ':' 
	{
		struct operand *op = $4;
		if (!IS_ITEMP($4))
		{
			op = newiTempOperand(operandType($4), FALSE);
			geniCodeAssign(op, $4);
		}
		$<op>$ = op;
		sprintf(labelbuffer, "%s%d", L_COND_END,  $<yyint>3);
		geniCodeGotoFromName(labelbuffer);
		sprintf(labelbuffer, "%s%d", L_COND_FALSE,  $<yyint>3);
		geniCodeLabelFromName(labelbuffer);
	}
	conditional_expr
	{ 
		$$ = $<op>6;
		geniCodeAssign($$, $7);
		sprintf(labelbuffer, "%s%d", L_COND_END,  $<yyint>3);
		geniCodeLabelFromName(labelbuffer);
	}
   ;

assignment_expr
   : conditional_expr
   | cast_expr assignment_operator assignment_expr
	{
		if (!$1 || !$3)
			$$ = NULL;
		else
		{
			operand *op;
			switch ($2) 
			{
				case '=':
					$$ = geniCodeAssign($1, $3);
					break;
				case MUL_ASSIGN:
					op = geniCodeMultiply($1, $3);
					$$ = geniCodeAssign($1, op);
					break;
				case DIV_ASSIGN:
					op = geniCodeDivision($1, $3);
					$$ = geniCodeAssign($1, op);
					break;
				case MOD_ASSIGN:
					op = geniCodeModulus($1, $3);
					$$ = geniCodeAssign($1, op);
					break;
				case ADD_ASSIGN:
					op = geniCodeAdd($1, $3);
					$$ = geniCodeAssign($1, op);
					break;
				case SUB_ASSIGN:
					op = geniCodeSubtract($1, $3);
					$$ = geniCodeAssign($1, op);
					break;
				case LEFT_ASSIGN:
					op = geniCodeLeftShift($1, $3);
					$$ = geniCodeAssign($1, op);
					break;
				case RIGHT_ASSIGN:
					op = geniCodeRightShift($1, $3);
					$$ = geniCodeAssign($1, op);
					break;
				case AND_ASSIGN:
					op = geniCodeBitwise($1, $3, '&');
					$$ = geniCodeAssign($1, op);
					break;
				case XOR_ASSIGN:
					op = geniCodeBitwise($1, $3, '^');
					$$ = geniCodeAssign($1, op);
					break;
				case OR_ASSIGN:
					op = geniCodeBitwise($1, $3, '|');
					$$ = geniCodeAssign($1, op);
					break;
				default :
					$$ = NULL;
			}
		}
	}
;

assignment_operator
   : '='             	{ $$ = '='; } 			
   | MUL_ASSIGN			
   | DIV_ASSIGN			
   | MOD_ASSIGN			
   | ADD_ASSIGN			
   | SUB_ASSIGN			
   | LEFT_ASSIGN		
   | RIGHT_ASSIGN		
   | AND_ASSIGN			
   | XOR_ASSIGN			
   | OR_ASSIGN			
   ;

expr
   : assignment_expr
   | expr ',' assignment_expr
		{ 
		}
   ;

constant_expr
   : conditional_expr
   ;

declaration
   : declaration_specifiers ';'
      {
         if (uselessDecl)
           yylexerror("useless declaration (possible use of keyword as variable name)");      
           
         uselessDecl = TRUE;
         $$ = NULL;	  
	  }  
   | declaration_specifiers init_declarator_list ';'
      {
         /* add the specifier list to the id */
         symbol *sym , *sym1;

         for (sym1 = sym = reverseSyms($2);sym != NULL;sym = sym->next) {
             sym_link *lnk = copyLinkChain($1);
             addDecl (sym,0,lnk) ;             
         }

         uselessDecl = TRUE;
         $$ = sym1 ;
	  } 
   ;
   
declaration_specifiers : declaration_specifiers_
;

declaration_specifiers_
   : storage_class_specifier
   | storage_class_specifier declaration_specifiers_  
   { 
     if ( !IS_SPEC($2)) 
	 {
       sym_link *lnk = $2 ;
       while (lnk && !IS_SPEC(lnk->next))
         lnk = lnk->next;
       lnk->next = mergeSpec($1,lnk->next, "storage_class_specifier declaration_specifiers - skipped");
       $$ = $2 ;
     }
     else
       $$ = mergeSpec($1,$2, "storage_class_specifier declaration_specifiers");   
   }
   | type_specifier  								
   | type_specifier declaration_specifiers_         
   { 
		if ( !IS_SPEC($2)) 
		{
			sym_link *lnk = $2 ;
			while (lnk && !IS_SPEC(lnk->next))
				lnk = lnk->next;
			lnk->next = mergeSpec($1,lnk->next, "type_specifier declaration_specifiers - skipped");
			$$ = $2 ;
		}
		else
			$$ = mergeSpec($1,$2, "type_specifier declaration_specifiers");   
   }
   | function_specifier                             
   | function_specifier declaration_specifiers_     { 
     if ( !IS_SPEC($2)) {
       sym_link *lnk = $2 ;
       while (lnk && !IS_SPEC(lnk->next))
         lnk = lnk->next;
       lnk->next = mergeSpec($1,lnk->next, "function_specifier declaration_specifiers - skipped");
       $$ = $2 ;
     }
     else
       $$ = mergeSpec($1,$2, "function_specifier declaration_specifiers");   
   }
   ;

init_declarator_list
   : init_declarator
   | init_declarator_list ',' init_declarator      
		{ 
			$3->next = $1 ; 
			$$ = $3;		
		}
   ;

init_declarator
   : declarator                  
		{ 
			 $$ = $1;		
		}
   | declarator '=' initializer  
		{		
			//int a[] = {};
			$$ = $1;
			if ($3)
				geniCodeAssign(operandFromSymbol($1), $3);
		}
   ;


storage_class_specifier
   : TYPEDEF	{
                  $$ = newLink (SPECIFIER) ;
                  SPEC_TYPEDEF($$) = 1 ;   
				}
   | EXTERN   	{
                  $$ = newLink (SPECIFIER) ;
                  SPEC_EXTR($$) = 1 ;   
				}
   | STATIC		{
                  $$ = newLink (SPECIFIER);
                  SPEC_STAT($$) = 1 ;   
				}
   | AUTO		{
                  $$ = newLink (SPECIFIER) ;
                  SPEC_SCLS($$) = S_AUTO  ;   
				} 
   | REGISTER	{
                  $$ = newLink (SPECIFIER);
                  SPEC_SCLS($$) = S_REGISTER ;   
				}
   ;

function_specifier
   : INLINE   { }
   ;

type_specifier
   : type_specifier2
   | type_specifier2 AT constant_expr
        {
		}
   ;

type_specifier2
   : CHAR      {
                  $$=newLink(SPECIFIER);
                  SPEC_NOUN($$) = V_CHAR  ;
                  ignoreTypedefType = 1;
			   }
   | SHORT     {
                  $$=newLink(SPECIFIER);
                  SPEC_SHORT($$) = 1 ;
                  ignoreTypedefType = 1;
			   }
   | INT       {
                  $$=newLink(SPECIFIER);
                  SPEC_NOUN($$) = V_INT   ;
                  ignoreTypedefType = 1;   
			   }
   | LONG      { 
                  $$=newLink(SPECIFIER);
                  SPEC_LONG($$) = 1       ;
                  ignoreTypedefType = 1;
			   }
   | SIGNED    {
                  $$=newLink(SPECIFIER);
                  $$->select.s.b_signed = 1;
                  ignoreTypedefType = 1;   
			   }
   | UNSIGNED  {
                  $$=newLink(SPECIFIER);
                  SPEC_USIGN($$) = 1      ;
                  ignoreTypedefType = 1;   
			   }
   | VOID      {
                  $$=newLink(SPECIFIER);
                  SPEC_NOUN($$) = V_VOID  ;
                  ignoreTypedefType = 1;   
			   }
   | CONST     {
                  $$=newLink(SPECIFIER);
                  SPEC_CONST($$) = 1;   
			   }
   | VOLATILE  {
                  $$=newLink(SPECIFIER);
                  SPEC_VOLATILE($$) = 1 ;   
			   }
   | BIT       { }
   | struct_or_union_specifier  {
                                   uselessDecl = FALSE;
                                   $$ = $1 ;
                                   ignoreTypedefType = 1;   
								}
   | enum_specifier     { }
   | TYPE_NAME 			{
							symbol *sym;
							sym_link   *p  ;
							sym = findSym(TypedefTab, NULL, $1) ;
							$$ = p = copyLinkChain(sym ? sym->type : NULL);
							SPEC_TYPEDEF(getSpec(p)) = 0;
							ignoreTypedefType = 1;   
						}
   ;
  
   //NVCUONG
struct_or_union_specifier
   : struct_or_union opt_stag 
        {
           if (!$2->type)
             {
               $2->type = $1;
             }
           else
             {
               if ($2->type != $1)
                 yylexerror("%s has bad tag '%s'", $1==STRUCT ? "struct" : "union", $2->tag);
             }
           
           /* add new struct to struct table */
		   addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0);             
        }
   '{' struct_declaration_list '}'
        {
           structdef *sdef ;
           symbol *sym, *dsym;

           for (sym=$5; sym; sym=sym->next) {
             if (IS_SPEC(sym->etype) && SPEC_SCLS(sym->etype)) {             
			   ccerror(sym->fileDef, sym->lineDef, "storage class is not allowed");               
               SPEC_SCLS(sym->etype) = 0;
             }
             for (dsym=sym->next; dsym; dsym=dsym->next) {
               if (*dsym->name && strcmp(sym->name, dsym->name)==0) {
				ccerror(sym->fileDef, sym->lineDef, "%s has duplicate member '%s'",
						$1==STRUCT ? "struct" : "union", sym->name);
               }
             }
           }

           /* Create a structdef   */
           sdef = $2 ;
           sdef->fields = reverseSyms($5) ;			/* link the fields */
           sdef->size  = compStructSize($1,sdef);   /* update size of  */
           promoteAnonStructs ($1, sdef);

           /* Create the specifier */
           $$ = newLink (SPECIFIER) ;
           SPEC_NOUN($$) = V_STRUCT;
           SPEC_STRUCT($$)= sdef ;
        }

   | struct_or_union stag
         {
			structdef *sdef;         
			symbol *sym;
			
			sym = newSymbol($2->tag, NestLevel);
			
			/* find structdef stag, if not exit show error */
            sdef = findSymWithBlock (StructTab,sym,currBlockno);            
            if (sdef)
            {
				$$ = newLink(SPECIFIER) ;
				SPEC_NOUN($$) = V_STRUCT;
				SPEC_STRUCT($$) = $2;

				if (!$2->type)
				 {
				   $2->type = $1;
				 }
				else
				 {
				   if ($2->type != $1)
					 yylexerror("%s has bad tag '%s'", $1==STRUCT ? "struct" : "union", $2->tag);
				 }
			}
			else
			{
				yylexerror("%s '%s' is not defined", $1==STRUCT ? "struct" : "union", $2->tag);			
				$$ = NULL;
			}
         }
   ;

struct_or_union
   : STRUCT          { $$ = STRUCT ; }
   | UNION           { $$ = UNION  ; }
   ;

opt_stag
: stag
|  	 {
		 $$ = newStruct(genSymName(NestLevel)) ;
		 $$->level = NestLevel ;
	 }
;

stag
:  identifier
    {
		 $$ = findSymWithBlock (StructTab,$1,currBlockno);
		 if (!$$) {
		   $$ = newStruct($1->name) ;
		   $$->level = NestLevel ;
		}			
	}
	
;


struct_declaration_list
   : struct_declaration
   | struct_declaration_list struct_declaration
       { 
           symbol *sym=$2;
           while (sym && sym->next) sym=sym->next;
           sym->next = $1 ;

           $$ = $2;
	   }   
   ;

struct_declaration
   : type_specifier_list struct_declarator_list ';'
       { 
           /* add this type to all the symbols */
           symbol *sym ;
           for ( sym = $2 ; sym != NULL ; sym = sym->next ) {
               sym_link *btype = copyLinkChain($1);

               /* make the symbol one level up */
               sym->level-- ;

               pointerTypes(sym->type,btype);
               if (!sym->type) {
                   sym->type = btype;
                   sym->etype = getSpec(sym->type);
               }
               else
                 addDecl (sym,0,btype);
               /* make sure the type is complete and sane */
               checkTypeSanity(sym->etype, sym->name);
           }
           ignoreTypedefType = 0;
           $$ = $2;
	   }
   ;

struct_declarator_list
   : struct_declarator
   | struct_declarator_list ',' struct_declarator
       {
           $3->next  = $1 ;
           $$ = $3 ;	   
	   }
   ;

struct_declarator
   : declarator
   | ':' constant_expr  {
							yylexerror("bit field is not accepted");
							exit(1);
						}
   | declarator ':' constant_expr
                        {
							yylexerror("bit field is not accepted");
							exit(1);
						}
   | { $$ = newSymbol ("", NestLevel) ; }
   ;

enum_specifier
   : ENUM            '{' enumerator_list '}' { fprintf(pFileRule, "\n enum_specifier -> ENUM            \'{\' enumerator_list \'}\'"); }
   | ENUM identifier '{' enumerator_list '}' { fprintf(pFileRule, "\n enum_specifier ->  ENUM identifier \'{\' enumerator_list \'}\'"); }
   | ENUM identifier                         { fprintf(pFileRule, "\n enum_specifier ->  ENUM identifier"); }
   ;

enumerator_list
    : enumerator	{ }
    | enumerator_list ','	{ }
    | enumerator_list ',' enumerator	
      { }
    ;

enumerator
    : identifier opt_assign_expr
     {  }
    ;

opt_assign_expr
   :  '='  constant_expr  { }
   |      {}             
   ;

declarator
   : declarator3
   		{ 
			$$ = $1 ; 
		}
   | pointer declarator3
         {
             addDecl ($2, 0, reverseLink($1));
             $$ = $2 ;
         }   
         
   ;

declarator3
   : declarator2_function_attributes
   		{
			 $$ = $1 ; 
		}
   | declarator2
   		{
			 $$ = $1 ; 
		}
		
   ;

function_declarator
   : declarator2_function_attributes    { $$ = $1; }
   | pointer declarator2_function_attributes
         {
             addDecl ($2,0,reverseLink($1));
             $$ = $2 ;
         }   
         
   ;

declarator2_function_attributes
   : function_declarator2             
   ;

declarator2
   : identifier
   | '(' declarator ')'
		{
			$$ = $2;   
		} 
   | declarator3 '[' ']'
		{
            sym_link   *p;

            p = newLink (DECLARATOR);
            DCL_TYPE(p) = ARRAY ;
            DCL_ELEM(p) = 0     ;
            addDecl($1,0,p);		
		} 
   | declarator3 '[' constant_expr ']'
         {
            sym_link *p;
            value *tval;
            int size;

			tval = $3->operand.valOperand;
			
			/* check operand value */
			assert(tval);
			
            /* if it is not a constant then Error  */
            p = newLink (DECLARATOR);
            DCL_TYPE(p) = ARRAY;

            if (!tval || !tval->etype || (SPEC_SCLS(tval->etype) != S_LITERAL))
              {
                yylexerror("internal error: constant are not expected");
                /* Assume a single item array to limit the cascade */
                /* of additional errors. */
                size = 1;
              }
            else
              {
                if ((size = (int) ulFromVal(tval)) < 0)
                  {
                    yylexerror("internal error: %s has negative array size %d", $1->name, size);
                    size = 1;
                  }
              }
            DCL_ELEM(p) = size;
            addDecl($1, 0, p);
         }   
   ;

function_declarator2
   : declarator2 '('   { currFunc = $1; }   ')'  { addDecl ($1,FUNCTION,NULL) ;}     
   | declarator2 '(' { currFunc = $1; NestLevel++; currBlockno++;} parameter_type_list ')'    
         {
             sym_link *funcType;

             addDecl ($1,FUNCTION,NULL) ;

             funcType = $1->type;
             DCL_FUNC_DCLED(funcType) = 0;
             
             while (funcType && !IS_FUNC(funcType))
               funcType = funcType->next;

             assert (funcType);
			 
             FUNC_HASVARARGS(funcType) = IS_VARG($4);
             FUNC_ARGS(funcType) = reverseVal($4);

             /* nest level was incremented to take care of the parms  */
             NestLevel-- ;
             currBlockno--;
             currFunc = $1;
             
             $$ = $1;
         }
   | declarator2 '(' identifier_list ')'
		{ 
           yylexerror("internal error: %s -> E_OLD_STYLE", $1->name) ;
           /* assume it returns an int */
           $1->type = $1->etype = newIntLink();
           $$ = $1 ;
		} 	
   ;

pointer
   : unqualified_pointer { $$ = $1; } 
   | unqualified_pointer type_specifier_list
		{
             $$ = $1  ;
             if (IS_SPEC($2)) {
                 DCL_TSPEC($1) = $2;
                 DCL_PTR_CONST($1) = SPEC_CONST($2);
                 DCL_PTR_VOLATILE($1) = SPEC_VOLATILE($2);
             }
             else
                 yylexerror ("internal error: pointer type is invalid");
		}    
   | unqualified_pointer pointer
         {
             $$ = $1 ;
             $$->next = $2 ;
             DCL_TYPE($2) = POINTER;
         }   
   | unqualified_pointer type_specifier_list pointer
         {
             $$ = $1 ;
             if (IS_SPEC($2)) {
                 DCL_TSPEC($1) = $2;             
                 DCL_PTR_CONST($1) = SPEC_CONST($2);
                 DCL_PTR_VOLATILE($1) = SPEC_VOLATILE($2);
                 DCL_TYPE($3) = POINTER;
             }
             else
                 yylexerror ("internal error: pointer type is invalid");
             $$->next = $3 ;
         }   
;

unqualified_pointer
   :  '*'
      {
        $$ = newLink(DECLARATOR);
        DCL_TYPE($$) = POINTER;
      }   
   ;

type_specifier_list 
	: type_specifier_list_ 
		{
			 $$ = $1; 		
		} 
	;

type_specifier_list_
   : type_specifier
   | type_specifier_list_ type_specifier
    {
     if ( !IS_SPEC($2)) {
       sym_link *lnk = $2 ;
       while (lnk && !IS_SPEC(lnk->next))
         lnk = lnk->next;
       lnk->next = mergeSpec($1,lnk->next, "type_specifier_list type_specifier skipped");
       $$ = $2 ;
     }
     else
       $$ = mergeSpec($1,$2, "type_specifier_list type_specifier");   
	   
	}
   ;

identifier_list
   : identifier
   | identifier_list ',' identifier
         {
           $3->next = $1;
           $$ = $3 ;
         }   
         
   ;
parameter_type_list
        : parameter_list
        | parameter_list ',' VAR_ARGS 	
		{ }
        ;

parameter_list
   : parameter_declaration
   | parameter_list ',' parameter_declaration
         {
            $3->next = $1 ;
            $$ = $3 ;
         }   
         
   ;

parameter_declaration
   : type_specifier_list declarator
		{
			  symbol *loop ;
			  value *args ;			  
			  symbol *func_params;
			  pointerTypes($2->type,$1);
			  addDecl ($2,0,$1);			 			  
			  for (loop=$2;loop;loop->_isparm=1, loop->localof = currFunc,loop=loop->next);			  			  
			  addSymChain (&$2);
			  $$ = symbolVal($2);
			  ignoreTypedefType = 0;   
		}
   | type_name 	
	   {
		  $$ = newValue() ;
		  $$->type = $1;
		  $$->etype = getSpec($$->type);
		  ignoreTypedefType = 0;	   
	   }
   ;

type_name
   : type_specifier_list  
		{
   			$$ = $1;
			ignoreTypedefType = 0;
		}
   | type_specifier_list abstract_declarator
               {
                 /* go to the end of the list */
                 sym_link *p;
                 pointerTypes($2,$1);
                 for ( p = $2 ; p && p->next ; p=p->next);
                 if (!p) {
                   yylexerror("syntax error '%s'", yytext);
                 } else {
                   p->next = $1 ;
                 }
                 $$ = $2 ;
                 ignoreTypedefType = 0;
               }   
   ;

abstract_declarator
   : pointer 		{ }
   | abstract_declarator2		{ }
   | pointer abstract_declarator2   		{ }
		
   ;

abstract_declarator2
   : '(' abstract_declarator ')'    	{ }
   | '[' ']'                        	{ fprintf(pFileRule, "\n abstract_declarator2 -> \'[\' \']\'"); }
   | '[' constant_expr ']'          	{ fprintf(pFileRule, "\n abstract_declarator2 -> \'[\' constant_expr \']\'"); }
   | abstract_declarator2 '[' ']'  		{ fprintf(pFileRule, "\n abstract_declarator2 -> abstract_declarator2 \'[\' \']\'"); }
   | abstract_declarator2 '[' constant_expr ']'		{ fprintf(pFileRule, "\n abstract_declarator2 -> abstract_declarator2 \'[\' constant_expr \']\'"); }
   | '(' ')'                        				{ fprintf(pFileRule, "\n abstract_declarator2 -> \'(\' \')\'"); }
   | '(' parameter_type_list ')'    				{ fprintf(pFileRule, "\n abstract_declarator2 -> \'(\' parameter_type_list \')\'"); }
   | abstract_declarator2 '(' ')' 					{ fprintf(pFileRule, "\n abstract_declarator2 -> abstract_declarator2 \'(\' \')\' "); }
   | abstract_declarator2 '(' {  } parameter_type_list ')' 		{ fprintf(pFileRule, "\n abstract_declarator2 -> abstract_declarator2 \'(\' {  } parameter_type_list \')\'"); }
   ;

initializer
   : assignment_expr            
   | '{'  initializer_list '}'      	{} 
   | '{'  initializer_list ',' '}'  	{} 	
   ;

initializer_list
   : initializer		 {}     
   | initializer_list ',' initializer  	{}    
   ;

statement
   : labeled_statement			{ } 
   | compound_statement			{ }
   | expression_statement		{ }
   | selection_statement		{ }
   | iteration_statement		{ }
   | jump_statement				{ }
   ;


labeled_statement
   : identifier ':'                   	{}
   | CASE constant_expr ':'				
	{
		if (STACK_ITEM(SwitchStack,0) && $2)
			add_case    ( STACK_ITEM(SwitchStack,0), $2, ++Case_label );
		sprintf(labelbuffer, "%s%d", L_SWITCH, Case_label);
		geniCodeLabelFromName(labelbuffer);
	}
   | DEFAULT ':'						
   {
		if (STACK_ITEM(SwitchStack,0))
			add_default_case( STACK_ITEM(SwitchStack,0), ++Case_label );
		sprintf(labelbuffer, "%s%d", L_SWITCH, Case_label);
		geniCodeLabelFromName(labelbuffer);
   }
   ;

start_block : '{'			 
		{ 
			STACK_PUSH(blockNum, currBlockno);
			currBlockno = ++blockNo ;
            ignoreTypedefType = 0;
		}
            ;

end_block   : '}'     		{ currBlockno = STACK_POP(blockNum); }
            ;

compound_statement
   : start_block end_block        					{  }             
   | start_block statement_list end_block     		{  }
   | start_block declaration_list end_block   		{  }
   | start_block declaration_list statement_list end_block		{  }                         
   | error ';'    									{ }                           
   ;

declaration_list
   : declaration			
		{		   
		   symbol *sym;
		   /* if this is typedef declare it immediately */
		   if ($1 && IS_TYPEDEF($1->etype)) {
			 $$ = NULL ;
		   }
		   else
			$$ = $1 ;
			
		   ignoreTypedefType = 0;
	   
    	   sym = findSym(TypedefTab, $1, $1->name);			
		   if (sym)
		   {
		      yylexerror("duplicate identifier name '%s' and typedef type '%s'", $1->name, ((symbol *)sym)->name);
		      $$ = NULL;
		   }
		   else
		   {
			   addSymChain(&$1);
			   allocVariables($1);		   
		   }
		}
   | declaration_list declaration
	{
       symbol   *sym;       
	   
       /* if this is a typedef */
       if ($2 && IS_TYPEDEF($2->etype)) {
         $$ = $1;
       }
       else {
                                /* get to the end of the previous decl */
         if ( $1 ) {
           $$ = sym = $1 ;
           while (sym->next)
             sym = sym->next ;
           sym->next = $2;
         }
         else
           $$ = $2 ;               
       }
       
       ignoreTypedefType = 0;
       
	   sym = findSym(TypedefTab, $2, $2->name);			
	   if (sym)
	   {
	      yylexerror("duplicate identifier name '%s' and typedef type '%s'", $2->name, ((symbol *)sym)->name);
	      $$ = $1;
	   }
	   else
	   {
		   addSymChain(&$2);
		   allocVariables($2);		   
	   }
	}
   ;

statement_list
   : statement
   | statement_list statement         	
	{ 
		
	}
   ;

expression_statement
   : ';'               	{ } 
   | expr ';'           { }
   ;

selection_statement
	: IF '(' test ')' statement 
	{
		sprintf(labelbuffer, "%s%d", L_NEXT, $3);
		geniCodeLabelFromName(labelbuffer);
	}
	| IF '(' test ')' statement ELSE 
	{
		sprintf(labelbuffer, "%s%d", L_ELSE, $3);
		geniCodeGotoFromName(labelbuffer);
		
		sprintf(labelbuffer, "%s%d", L_NEXT, $3);
		geniCodeLabelFromName(labelbuffer);
	}
	statement
	{
		sprintf(labelbuffer, "%s%d", L_ELSE, $3);
		geniCodeLabelFromName(labelbuffer);
	}
	| SWITCH '(' expr ')'  
	{
		if ($3)
			STACK_PUSH( SwitchStack,	new_stab($3, ++Case_label) );
		sprintf(labelbuffer, "%s%d", L_SWITCH, Case_label);
		geniCodeGotoFromName(labelbuffer);
		STACK_PUSH(BreakStack,	   ++Case_label);
		STACK_PUSH(BreakLabelStack, L_SWITCH);
	}statement  	
	{
		if (STACK_ITEM(SwitchStack,0))
			gen_stab_and_free_table( STACK_POP(SwitchStack) );
		STACK_POP(BreakStack);
		STACK_POP(BreakLabelStack);
	}    
   ;
   
test	:	{   static int label = 0;
				sprintf(labelbuffer, "%s%d", L_TEST, $<yyint>$ = ++label);
				geniCodeLabelFromName(labelbuffer);
			}
	  expr	{
				struct symbol *_falseLab;
				$$ = $<yyint>1;
				sprintf(labelbuffer, "%s%d", L_NEXT, $$);
				_falseLab = newSymbol(labelbuffer, NestLevel);
				if ($2)
					geniCodeIfx($2, NULL, _falseLab); 		
			}
	| /* empty */		{   $$ = 0;  /* no test */
				}
	;


iteration_statement
   : WHILE '(' test ')' 
	{
		seqPointNo++;
		STACK_PUSH(ContinueStack, $3);
		STACK_PUSH(ContinueLabelStack, L_TEST);
		STACK_PUSH(BreakStack, $3);
		STACK_PUSH(BreakLabelStack, L_NEXT);
	}
	statement	
	{
		sprintf(labelbuffer, "%s%d", L_TEST, $3);
		geniCodeGotoFromName(labelbuffer);
		sprintf(labelbuffer, "%s%d", L_NEXT, $3);
		geniCodeLabelFromName(labelbuffer);
		STACK_POP(ContinueStack);
		STACK_POP(ContinueLabelStack);
		STACK_POP(BreakStack);
		STACK_POP(BreakLabelStack);
	}
   | DO 
	{
		static int label;
		sprintf(labelbuffer, "%s%d", L_DOTOP, $<yyint>$ = ++label);
		geniCodeLabelFromName(labelbuffer);
		STACK_PUSH(ContinueStack, label);
		STACK_PUSH(ContinueLabelStack, L_DOTEST);
		STACK_PUSH(BreakStack, label);
		STACK_PUSH(BreakLabelStack, L_DOEXIT);
	}
	statement   WHILE 
	{
		sprintf(labelbuffer, "%s%d", L_DOTEST, $<yyint>2);
		geniCodeLabelFromName(labelbuffer);
	}
	'(' test ')' ';'		
	{
		seqPointNo++;
		sprintf(labelbuffer, "%s%d", L_DOTOP, $<yyint>2);
		geniCodeGotoFromName(labelbuffer);
		sprintf(labelbuffer, "%s%d", L_DOEXIT, $<yyint>2);
		geniCodeLabelFromName(labelbuffer);
		sprintf(labelbuffer, "%s%d", L_NEXT, $7);
		geniCodeLabelFromName(labelbuffer);
		STACK_POP(ContinueStack);
		STACK_POP(ContinueLabelStack);
		STACK_POP(BreakStack);
		STACK_POP(BreakLabelStack);
	}
   | FOR '(' expr_opt   ';' test ';' 
	{
		sprintf(labelbuffer, "%s%d", L_BODY, $5);
		geniCodeGotoFromName(labelbuffer);
		sprintf(labelbuffer, "%s%d", L_INCREMENT, $5);
		geniCodeLabelFromName(labelbuffer);
		STACK_PUSH(ContinueStack,	$5);
		STACK_PUSH(ContinueLabelStack,	L_INCREMENT );
		STACK_PUSH(BreakStack,	$5);
		STACK_PUSH(BreakLabelStack,	L_NEXT);
	}
   expr_opt ')'  
   {
		sprintf(labelbuffer, "%s%d", L_TEST, $5);
		geniCodeGotoFromName(labelbuffer);
		sprintf(labelbuffer, "%s%d", L_BODY, $5);
		geniCodeLabelFromName(labelbuffer);
   }
   statement
	{ 
		sprintf(labelbuffer, "%s%d", L_INCREMENT, $5);
		geniCodeGotoFromName(labelbuffer);
		sprintf(labelbuffer, "%s%d", L_NEXT, $5);
		geniCodeLabelFromName(labelbuffer);
		STACK_POP(ContinueStack,	$5);
		STACK_POP(ContinueLabelStack,	L_INCREMENT );
		STACK_POP(BreakStack,	$5);
		STACK_POP(BreakLabelStack,	L_NEXT);
	}
;

expr_opt
        :/* epsilon */   { $$ = NULL ; seqPointNo++; }
        |       expr     { $$ = $1 ; seqPointNo++; }
        ;

jump_statement
   : GOTO identifier ';'    	
	{ 
		geniCodeGotoFromName($2);
	}  
   | CONTINUE ';'          	 	
	{ 
		if (STACK_EMPTY(ContinueStack))
		{
			SYNTAX_ERROR;
		}
		else
		{
			sprintf(labelbuffer, "%s%d", STACK_ITEM(ContinueLabelStack, 0), STACK_ITEM(ContinueStack, 0));
			geniCodeGotoFromName(labelbuffer);
		}
	} 
   | BREAK ';'             		
	{ 
		if (STACK_EMPTY(BreakStack))
		{
			SYNTAX_ERROR;
		}
		else
		{
			sprintf(labelbuffer, "%s%d", STACK_ITEM(BreakLabelStack, 0), STACK_ITEM(BreakStack, 0));
			geniCodeGotoFromName(labelbuffer);
		}
	} 
   | RETURN ';'           		 
	{ 
		geniCodeReturn(NULL);
	}  
   | RETURN expr ';' 			      
   {
		if ($2)
			geniCodeReturn($2);
   }
   ;

identifier
   : IDENTIFIER   
		{ 
			$$ = newSymbol($1, NestLevel);
		}  
   ;
%%
